#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import time
import json
import yaml
import logging
import socket
import threading
import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
from datetime import datetime
from logging.handlers import RotatingFileHandler
import paho.mqtt.client as mqtt

# 设置中文字体支持
import matplotlib
matplotlib.use('Agg')  # 非交互式后端
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 导入原有的网关逻辑
from tcp2mqtt_gateway import TCP2MQTTGateway

class TCP2MQTTGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("TCP2MQTT 协议转换网关")
        self.root.geometry("1000x700")
        self.root.resizable(True, True)
        
        # 设置图标（可选）
        # self.root.iconbitmap("icon.ico")
        
        # 确保中文显示正常
        self.setup_fonts()
        
        # 初始化变量
        self.gateway = None
        self.gateway_thread = None
        self.running = False
        self.config_file = "config.yaml"
        self.config_data = {}
        
        # 先创建状态栏，确保在其他方法使用前已存在
        self.create_status_bar()
        
        # 创建主框架
        self.create_main_frame()
        
        # 尝试加载默认配置
        self.load_config_file(self.config_file)
        
        # 设置关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
    
    def setup_fonts(self):
        """设置字体确保中文显示正常"""
        # Tkinter会自动使用系统字体，这里可以根据需要调整
        pass
    
    def create_main_frame(self):
        """创建主界面布局"""
        # 创建选项卡控件
        self.tab_control = ttk.Notebook(self.root)
        
        # 创建各个选项卡
        self.tab_dashboard = ttk.Frame(self.tab_control)
        self.tab_config = ttk.Frame(self.tab_control)
        self.tab_logs = ttk.Frame(self.tab_control)
        self.tab_connections = ttk.Frame(self.tab_control)
        
        # 添加选项卡到选项卡控件
        self.tab_control.add(self.tab_dashboard, text="控制面板")
        self.tab_control.add(self.tab_config, text="配置管理")
        self.tab_control.add(self.tab_logs, text="日志查看")
        self.tab_control.add(self.tab_connections, text="连接状态")
        
        # 放置选项卡控件
        self.tab_control.pack(expand=1, fill="both")
        
        # 创建各个选项卡的内容
        self.create_dashboard_tab()
        self.create_config_tab()
        self.create_logs_tab()
        self.create_connections_tab()
        
        # 创建状态栏
        self.create_status_bar()
    
    def create_dashboard_tab(self):
        """创建控制面板"""
        # 创建主框架
        main_frame = ttk.Frame(self.tab_dashboard, padding="10")
        main_frame.pack(fill="both", expand=True)
        
        # 创建左侧信息面板
        info_frame = ttk.LabelFrame(main_frame, text="网关状态", padding="10")
        info_frame.pack(side="left", fill="both", expand=True, padx=5, pady=5)
        
        # 创建状态标签
        self.status_var = tk.StringVar(value="未启动")
        status_label = ttk.Label(info_frame, text="网关状态:")
        status_value_label = ttk.Label(info_frame, textvariable=self.status_var, font=("SimHei", 12, "bold"))
        
        self.mqtt_status_var = tk.StringVar(value="未连接")
        mqtt_status_label = ttk.Label(info_frame, text="MQTT连接:")
        mqtt_status_value_label = ttk.Label(info_frame, textvariable=self.mqtt_status_var)
        
        self.tcp_count_var = tk.StringVar(value="0")
        tcp_count_label = ttk.Label(info_frame, text="TCP连接数:")
        tcp_count_value_label = ttk.Label(info_frame, textvariable=self.tcp_count_var)
        
        # 使用网格布局
        status_label.grid(row=0, column=0, sticky="w", pady=5)
        status_value_label.grid(row=0, column=1, sticky="w", pady=5)
        
        mqtt_status_label.grid(row=1, column=0, sticky="w", pady=5)
        mqtt_status_value_label.grid(row=1, column=1, sticky="w", pady=5)
        
        tcp_count_label.grid(row=2, column=0, sticky="w", pady=5)
        tcp_count_value_label.grid(row=2, column=1, sticky="w", pady=5)
        
        # 添加分隔线
        separator = ttk.Separator(info_frame, orient="horizontal")
        separator.grid(row=3, column=0, columnspan=2, sticky="ew", pady=10)
        
        # 创建设置文件路径选择
        config_frame = ttk.Frame(info_frame)
        config_frame.grid(row=4, column=0, columnspan=2, sticky="ew", pady=5)
        
        ttk.Label(config_frame, text="配置文件:").pack(side="left", padx=5)
        self.config_path_var = tk.StringVar(value="config.yaml")
        ttk.Entry(config_frame, textvariable=self.config_path_var, width=30).pack(side="left", fill="x", expand=True, padx=5)
        ttk.Button(config_frame, text="浏览...", command=self.browse_config_file).pack(side="left", padx=5)
        
        # 创建右侧控制按钮面板
        control_frame = ttk.LabelFrame(main_frame, text="控制", padding="10")
        control_frame.pack(side="right", fill="y", padx=5, pady=5)
        
        # 创建控制按钮
        self.start_button = ttk.Button(control_frame, text="启动网关", command=self.start_gateway, width=20)
        self.stop_button = ttk.Button(control_frame, text="停止网关", command=self.stop_gateway, width=20, state="disabled")
        self.reload_config_button = ttk.Button(control_frame, text="重新加载配置", command=self.reload_config, width=20)
        self.show_debug_info_button = ttk.Button(control_frame, text="显示调试信息", command=self.show_debug_info, width=20)
        
        # 放置按钮
        self.start_button.pack(pady=10)
        self.stop_button.pack(pady=10)
        self.reload_config_button.pack(pady=10)
        self.show_debug_info_button.pack(pady=10)
        
        # 添加一些间距
        control_frame.pack_propagate(False)
    
    def create_config_tab(self):
        """创建配置管理选项卡"""
        # 创建主框架
        main_frame = ttk.Frame(self.tab_config, padding="10")
        main_frame.pack(fill="both", expand=True)
        
        # 创建左侧选项列表
        left_frame = ttk.Frame(main_frame, width=200)
        left_frame.pack(side="left", fill="y", padx=5, pady=5)
        
        # 创建配置类别列表
        self.config_category_var = tk.StringVar()
        config_categories = ["MQTT配置", "TCP配置", "心跳配置", "映射配置", "日志配置"]
        self.config_category_listbox = tk.Listbox(left_frame, listvariable=self.config_category_var, height=15, width=20)
        self.config_category_listbox.pack(fill="both", expand=True)
        
        # 填充列表
        for category in config_categories:
            self.config_category_listbox.insert(tk.END, category)
        
        # 绑定选择事件
        self.config_category_listbox.bind('<<ListboxSelect>>', self.on_config_category_selected)
        
        # 创建右侧配置编辑区域
        self.config_edit_frame = ttk.Frame(main_frame)
        self.config_edit_frame.pack(side="right", fill="both", expand=True, padx=5, pady=5)
        
        # 创建配置编辑区域的内容（将在选择类别时动态创建）
        self.config_entries = {}
        
        # 添加保存按钮
        save_frame = ttk.Frame(main_frame)
        save_frame.pack(side="bottom", fill="x", padx=5, pady=10)
        
        self.save_config_button = ttk.Button(save_frame, text="保存配置", command=self.save_config, width=20)
        self.save_config_button.pack(side="right", padx=5)
    
    def create_logs_tab(self):
        """创建日志查看选项卡"""
        # 创建主框架
        main_frame = ttk.Frame(self.tab_logs, padding="10")
        main_frame.pack(fill="both", expand=True)
        
        # 创建日志级别选择
        level_frame = ttk.Frame(main_frame)
        level_frame.pack(fill="x", padx=5, pady=5)
        
        ttk.Label(level_frame, text="日志级别:").pack(side="left", padx=5)
        self.log_level_var = tk.StringVar(value="INFO")
        log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
        level_combo = ttk.Combobox(level_frame, textvariable=self.log_level_var, values=log_levels, state="readonly")
        level_combo.pack(side="left", padx=5)
        level_combo.bind('<<ComboboxSelected>>', self.on_log_level_changed)
        
        # 创建刷新按钮
        self.refresh_log_button = ttk.Button(level_frame, text="刷新日志", command=self.refresh_logs)
        self.refresh_log_button.pack(side="right", padx=5)
        
        # 创建日志文本区域
        self.log_text = scrolledtext.ScrolledText(main_frame, wrap=tk.WORD, font=("SimHei", 10))
        self.log_text.pack(fill="both", expand=True, padx=5, pady=5)
        self.log_text.config(state="disabled")
        
        # 先创建空日志文件，避免首次启动时找不到文件
        log_file = "tcp2mqtt.log"
        if not os.path.exists(log_file):
            try:
                with open(log_file, 'w', encoding='utf-8') as f:
                    f.write(f"# 日志文件创建于 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            except Exception as e:
                self.update_status(f"创建日志文件失败: {str(e)}")
        
        # 初始加载日志
        self.refresh_logs()
    
    def create_connections_tab(self):
        """创建连接状态选项卡"""
        # 创建主框架
        main_frame = ttk.Frame(self.tab_connections, padding="10")
        main_frame.pack(fill="both", expand=True)
        
        # 创建连接状态树视图
        columns = ("device_id", "status", "last_active", "ip", "port")
        self.connections_tree = ttk.Treeview(main_frame, columns=columns, show="headings")
        
        # 定义列
        self.connections_tree.heading("device_id", text="设备ID")
        self.connections_tree.heading("status", text="状态")
        self.connections_tree.heading("last_active", text="最后活动时间")
        self.connections_tree.heading("ip", text="IP地址")
        self.connections_tree.heading("port", text="端口")
        
        # 设置列宽
        self.connections_tree.column("device_id", width=150)
        self.connections_tree.column("status", width=80)
        self.connections_tree.column("last_active", width=180)
        self.connections_tree.column("ip", width=120)
        self.connections_tree.column("port", width=80)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=self.connections_tree.yview)
        self.connections_tree.configure(yscroll=scrollbar.set)
        
        # 放置树视图和滚动条
        self.connections_tree.pack(side="left", fill="both", expand=True, padx=5, pady=5)
        scrollbar.pack(side="right", fill="y", pady=5)
        
        # 创建刷新按钮
        refresh_frame = ttk.Frame(main_frame)
        refresh_frame.pack(side="bottom", fill="x", padx=5, pady=10)
        
        self.refresh_connections_button = ttk.Button(refresh_frame, text="刷新连接状态", command=self.refresh_connections)
        self.refresh_connections_button.pack(side="right", padx=5)
    
    def create_status_bar(self):
        """创建状态栏"""
        self.status_bar = ttk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side="bottom", fill="x")
    
    def browse_config_file(self):
        """浏览并选择配置文件"""
        file_path = filedialog.askopenfilename(
            title="选择配置文件",
            filetypes=[("YAML文件", "*.yaml"), ("所有文件", "*")]
        )
        if file_path:
            self.config_path_var.set(file_path)
            self.load_config_file(file_path)
    
    def load_config_file(self, file_path):
        """加载配置文件"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                self.config_data = yaml.safe_load(f)
            
            self.config_file = file_path
            self.config_path_var.set(file_path)
            
            # 更新状态栏
            self.update_status(f"已加载配置文件: {os.path.basename(file_path)}")
            
            # 如果有选中的配置类别，刷新编辑区域
            if self.config_category_listbox.curselection():
                self.on_config_category_selected(None)
                
        except Exception as e:
            messagebox.showerror("错误", f"加载配置文件失败: {str(e)}")
            self.update_status("加载配置文件失败")
    
    def on_config_category_selected(self, event):
        """当选择配置类别时更新编辑区域"""
        # 清空现有编辑区域
        for widget in self.config_edit_frame.winfo_children():
            widget.destroy()
        
        self.config_entries.clear()
        
        # 获取选中的类别
        selection = self.config_category_listbox.curselection()
        if not selection:
            return
        
        index = selection[0]
        category_map = {
            0: "mqtt",
            1: "tcp", 
            2: "heartbeat",
            3: "mapping",
            4: "logging"
        }
        
        category_key = category_map.get(index)
        if not category_key or category_key not in self.config_data:
            return
        
        # 创建配置项
        category_data = self.config_data[category_key]
        row = 0
        
        for key, value in category_data.items():
            # 创建标签
            label = ttk.Label(self.config_edit_frame, text=f"{key}:")
            label.grid(row=row, column=0, sticky="w", padx=5, pady=5)
            
            # 根据值的类型创建相应的输入控件
            if isinstance(value, bool):
                var = tk.BooleanVar(value=value)
                entry = ttk.Checkbutton(self.config_edit_frame, variable=var)
            elif isinstance(value, int):
                var = tk.StringVar(value=str(value))
                entry = ttk.Entry(self.config_edit_frame, textvariable=var, width=50)
            else:
                var = tk.StringVar(value=str(value))
                entry = ttk.Entry(self.config_edit_frame, textvariable=var, width=50)
            
            entry.grid(row=row, column=1, sticky="ew", padx=5, pady=5)
            self.config_entries[key] = (var, entry)
            
            row += 1
        
        # 配置列权重，使输入框能够拉伸
        self.config_edit_frame.columnconfigure(1, weight=1)
    
    def save_config(self):
        """保存配置"""
        try:
            # 获取选中的类别
            selection = self.config_category_listbox.curselection()
            if not selection:
                messagebox.showinfo("提示", "请先选择要保存的配置类别")
                return
            
            index = selection[0]
            category_map = {
                0: "mqtt",
                1: "tcp", 
                2: "heartbeat",
                3: "mapping",
                4: "logging"
            }
            
            category_key = category_map.get(index)
            if not category_key:
                return
            
            # 更新配置数据
            for key, (var, _) in self.config_entries.items():
                # 根据原始值类型转换
                original_value = self.config_data[category_key].get(key)
                if isinstance(original_value, bool):
                    self.config_data[category_key][key] = var.get()
                elif isinstance(original_value, int):
                    try:
                        self.config_data[category_key][key] = int(var.get())
                    except ValueError:
                        messagebox.showerror("错误", f"{key}必须是整数")
                        return
                else:
                    self.config_data[category_key][key] = var.get()
            
            # 保存到文件
            with open(self.config_file, 'w', encoding='utf-8') as f:
                yaml.dump(self.config_data, f, default_flow_style=False, allow_unicode=True)
            
            messagebox.showinfo("成功", "配置已保存")
            self.update_status(f"已保存配置到: {os.path.basename(self.config_file)}")
            
            # 如果网关正在运行，提示重新加载
            if self.running:
                if messagebox.askyesno("提示", "配置已更新，是否重新加载配置？"):
                    self.reload_config()
                    
        except Exception as e:
            messagebox.showerror("错误", f"保存配置失败: {str(e)}")
            self.update_status("保存配置失败")
    
    def reload_config(self):
        """重新加载配置"""
        try:
            self.load_config_file(self.config_file)
            
            # 如果网关正在运行，需要重启以应用新配置
            if self.running:
                self.stop_gateway()
                time.sleep(1)  # 等待网关完全停止
                self.start_gateway()
                
        except Exception as e:
            messagebox.showerror("错误", f"重新加载配置失败: {str(e)}")
    
    def start_gateway(self):
        """启动网关"""
        try:
            # 禁用启动按钮，启用停止按钮
            self.start_button.config(state="disabled")
            self.stop_button.config(state="normal")
            
            # 更新状态
            self.update_status("正在启动网关...")
            self.status_var.set("启动中")
            self.root.update()
            
            # 创建并启动网关
            self.gateway = TCP2MQTTGateway(self.config_file)
            self.running = True
            
            # 在新线程中运行网关
            self.gateway_thread = threading.Thread(target=self.run_gateway)
            self.gateway_thread.daemon = True
            self.gateway_thread.start()
            
            # 启动状态更新线程
            self.status_update_thread = threading.Thread(target=self.update_gateway_status)
            self.status_update_thread.daemon = True
            self.status_update_thread.start()
            
            # 自动切换到日志标签页
            self.tab_control.select(self.tab_logs)
            
        except Exception as e:
            messagebox.showerror("错误", f"启动网关失败: {str(e)}")
            self.start_button.config(state="normal")
            self.stop_button.config(state="disabled")
            self.status_var.set("未启动")
    
    def run_gateway(self):
        """在单独线程中运行网关"""
        try:
            self.gateway.start()
        except Exception as e:
            print(f"网关运行错误: {str(e)}")
            self.running = False
            
            # 在主线程中更新UI
            def update_ui():
                self.start_button.config(state="normal")
                self.stop_button.config(state="disabled")
                self.status_var.set("错误")
                self.update_status(f"网关运行错误: {str(e)}")
            
            self.root.after(0, update_ui)
    
    def stop_gateway(self):
        """停止网关"""
        try:
            # 更新状态
            self.update_status("正在停止网关...")
            self.status_var.set("停止中")
            self.root.update()
            
            # 停止网关
            if self.gateway:
                self.running = False
                self.gateway.stop()
                self.gateway = None
            
            # 等待线程结束
            if self.gateway_thread and self.gateway_thread.is_alive():
                self.gateway_thread.join(timeout=5.0)
            
            # 更新按钮状态
            self.start_button.config(state="normal")
            self.stop_button.config(state="disabled")
            
            # 更新状态
            self.status_var.set("已停止")
            self.mqtt_status_var.set("未连接")
            self.tcp_count_var.set("0")
            self.update_status("网关已停止")
            
            # 清空连接状态
            self.clear_connections_tree()
            
        except Exception as e:
            messagebox.showerror("错误", f"停止网关失败: {str(e)}")
    
    def update_gateway_status(self):
        """定期更新网关状态"""
        while self.running and self.gateway:
            try:
                # 更新MQTT连接状态
                if hasattr(self.gateway, 'mqtt_client'):
                    connected = self.gateway.mqtt_client.is_connected()
                    status = "已连接" if connected else "未连接"
                    self.root.after(0, lambda s=status: self.mqtt_status_var.set(s))
                
                # 更新TCP连接数
                if hasattr(self.gateway, 'tcp_connections'):
                    count = len(self.gateway.tcp_connections)
                    self.root.after(0, lambda c=str(count): self.tcp_count_var.set(c))
                
                # 更新网关状态
                self.root.after(0, lambda: self.status_var.set("运行中"))
                
                # 刷新连接状态
                self.root.after(0, self.refresh_connections)
                
            except Exception as e:
                print(f"更新状态错误: {str(e)}")
            
            time.sleep(2)  # 每2秒更新一次
    
    def refresh_connections(self):
        """刷新连接状态"""
        # 清空树视图
        self.clear_connections_tree()
        
        # 如果网关未运行，直接返回
        if not self.running or not hasattr(self.gateway, 'tcp_connections'):
            return
        
        # 添加连接信息
        for device_id, (conn, last_active) in self.gateway.tcp_connections.items():
            # 获取连接信息
            try:
                addr, port = conn.getpeername()
            except:
                addr, port = "未知", "未知"
            
            # 格式化最后活动时间
            last_active_str = datetime.fromtimestamp(last_active).strftime("%Y-%m-%d %H:%M:%S")
            
            # 添加到树视图
            self.connections_tree.insert("", tk.END, values=(device_id, "活跃", last_active_str, addr, port))
    
    def clear_connections_tree(self):
        """清空连接树视图"""
        for item in self.connections_tree.get_children():
            self.connections_tree.delete(item)
    
    def refresh_logs(self):
        """刷新日志显示"""
        log_file = self.config_data.get('logging', {}).get('file', 'tcp2mqtt.log')
        
        try:
            # 读取日志文件
            with open(log_file, 'r', encoding='utf-8') as f:
                logs = f.readlines()
            
            # 过滤日志级别
            log_level = self.log_level_var.get()
            level_map = {
                "DEBUG": 10,
                "INFO": 20,
                "WARNING": 30,
                "ERROR": 40,
                "CRITICAL": 50
            }
            
            min_level = level_map.get(log_level, 20)
            filtered_logs = []
            
            for log in logs:
                # 简单的日志级别过滤
                if "- DEBUG -" in log and min_level <= 10:
                    filtered_logs.append(log)
                elif "- INFO -" in log and min_level <= 20:
                    filtered_logs.append(log)
                elif "- WARNING -" in log and min_level <= 30:
                    filtered_logs.append(log)
                elif "- ERROR -" in log and min_level <= 40:
                    filtered_logs.append(log)
                elif "- CRITICAL -" in log and min_level <= 50:
                    filtered_logs.append(log)
            
            # 更新日志文本区域
            self.log_text.config(state="normal")
            self.log_text.delete(1.0, tk.END)
            self.log_text.insert(tk.END, "".join(filtered_logs))
            self.log_text.see(tk.END)  # 滚动到底部
            self.log_text.config(state="disabled")
            
        except Exception as e:
            self.update_status(f"读取日志文件失败: {str(e)}")
    
    def on_log_level_changed(self, event):
        """当日志级别改变时刷新日志"""
        self.refresh_logs()
    
    def show_debug_info(self):
        """显示调试信息对话框"""
        if not self.gateway:
            messagebox.showinfo("提示", "网关未运行")
            return
        
        # 创建调试信息对话框
        debug_window = tk.Toplevel(self.root)
        debug_window.title("调试信息")
        debug_window.geometry("600x400")
        
        # 创建文本区域
        debug_text = scrolledtext.ScrolledText(debug_window, wrap=tk.WORD)
        debug_text.pack(fill="both", expand=True, padx=10, pady=10)
        
        # 收集调试信息
        info = ["======= TCP2MQTT 网关调试信息 =======\n"]
        
        # MQTT连接状态
        mqtt_connected = self.gateway.mqtt_client.is_connected() if hasattr(self.gateway, 'mqtt_client') else False
        info.append(f"MQTT连接状态: {'已连接' if mqtt_connected else '未连接'}\n")
        
        # TCP连接信息
        if hasattr(self.gateway, 'tcp_connections'):
            info.append(f"TCP连接数量: {len(self.gateway.tcp_connections)}\n")
            for device_id, (conn, last_active) in self.gateway.tcp_connections.items():
                try:
                    addr, port = conn.getpeername()
                except:
                    addr, port = "未知", "未知"
                
                time_diff = time.time() - last_active
                info.append(f"  设备 {device_id}: 地址={addr}:{port}, 最后活动={time_diff:.2f}秒前\n")
        
        # 配置信息
        info.append("\n配置信息:\n")
        info.append(f"  配置文件: {self.config_file}\n")
        info.append(f"  MQTT Broker: {self.config_data.get('mqtt', {}).get('broker', '未配置')}\n")
        info.append(f"  TCP服务: {self.config_data.get('tcp', {}).get('remote_host', '未配置')}:{self.config_data.get('tcp', {}).get('remote_port', '未配置')}\n")
        
        # 显示调试信息
        debug_text.insert(tk.END, "".join(info))
        debug_text.config(state="disabled")
    
    def update_status(self, message):
        """更新状态栏消息"""
        self.status_bar.config(text=message)
    
    def on_closing(self):
        """窗口关闭事件处理"""
        if self.running:
            if messagebox.askyesno("确认", "网关正在运行，确定要关闭吗？"):
                self.stop_gateway()
                self.root.destroy()
        else:
            self.root.destroy()

def main():
    """主函数"""
    print("正在启动TCP2MQTT Gateway GUI...")
    try:
        root = tk.Tk()
        print("Tkinter根窗口创建成功")
        app = TCP2MQTTGUI(root)
        print("GUI应用实例创建成功")
        print("进入主事件循环...")
        root.mainloop()
        print("主事件循环已退出")
    except Exception as e:
        print(f"GUI启动过程中发生错误: {str(e)}")
        import traceback
        traceback.print_exc()

if __name__ == "__main__":
    main()